home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1995…tember: Reference Library / Dev.CD Sep 95 RL / Dev.CD Sep 95 RL.toast / mac / Technical Documentation / develop / develop Issue 23 code / Documentary Synchronicity ƒ / Source ƒ / Support ƒ / SaveSafely.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-06-10  |  11.1 KB  |  407 lines  |  [TEXT/KAHL]

  1. /* 4567890123456789012345678901234567890123456789012345678901234567 */
  2. #include <Dialogs.h>
  3. #include <Folders.h>
  4. #include <Script.h>
  5. #include <Resources.h>
  6. #include <TextUtils.h>
  7.  
  8. #include "EvenMoreFiles.h"
  9. #include "DSDocumentList.h"
  10. #include "ResourceDefinitions.h"
  11. #include "Toolbox.h"
  12.  
  13. #include "SaveSafely.h"
  14.  
  15. #define kKilobytesPerByte (1024)
  16.  
  17. static OSErr CopyUnknownResTypes(short aSourceResFile, 
  18.     short aDestinationResFile, ResType *aTypeList, short aTypeCount);
  19. static OSErr GetTemporaryName(Str255 theTemporaryName);
  20. static Boolean InTypeList(ResType aType, short aTypeCount, 
  21.     ResType *aList);
  22. static OSErr GetTrashSpace(short aVRefNum, 
  23.     unsigned long *aTrashSpace);
  24.  
  25. OSErr DSGetFileSize(unsigned long *aFileSize);
  26.  
  27. #if STRICT_WINDOWS
  28. OSErr SaveSafely(WindowRef aWindow, ResType *aTypeList, 
  29.         short aTypeCount, WriteDataProcPtr aWriteDFProc,
  30.         WriteDataProcPtr aWriteRFProc) {
  31. #else
  32. OSErr SaveSafely(WindowPtr aWindow, ResType *aTypeList, 
  33.         short aTypeCount, WriteDataProcPtr aWriteDFProc,
  34.         WriteDataProcPtr aWriteRFProc) {
  35. #endif
  36.     OSErr theError = noErr;
  37.     unsigned long theFileSize, theSpaceAvailable, theTrashSpace;
  38.     FSSpec theOriginalFile, theTemporaryFile;
  39.     short theTemporaryDFRefNum, theTemporaryRFRefNum;
  40.     short theOriginalDFRefNum, theOriginalRFRefNum;
  41.     short theTemporaryItemsVRefNum;
  42.     long theTemporaryItemsDirID, theTemporaryDirID;
  43.     Str255 theTemporaryName;
  44.     OSType theTemporaryCreator, theTemporaryFileType;
  45.  
  46. /*
  47.  * Get the file reference number of the data fork of the file 
  48.  * currently associated with the window to save.
  49.  */
  50.     theError = DSGetWindowDFRefNum(aWindow, &theOriginalDFRefNum);
  51.     if (theError == noErr) {
  52. /*
  53.  * Get the file reference number of the resource fork of the file 
  54.  * currently associated with the window to save.
  55.  */
  56.         theError = DSGetWindowRFRefNum(aWindow, &theOriginalRFRefNum);
  57.     }
  58.     if (theError == noErr) {
  59. /*
  60.  * Get the FSSpec for the original file from the reference number.
  61.  */
  62.         theError = GetFileSpec(theOriginalDFRefNum, &theOriginalFile);
  63.     }
  64.     if (theError == noErr) {
  65. /*
  66.  * Determine the final size of the new file.
  67.  */
  68.         theError = DSGetFileSize(&theFileSize);
  69.     }
  70.     if (theError == noErr) {
  71. /*
  72.  * Determine the amount of free space on the volume.
  73.  */
  74.         theError = GetFreeBytesOnVolume(theOriginalFile.vRefNum, 
  75.             &theSpaceAvailable);
  76.     }
  77.     if (theError == noErr) {
  78.         if (theFileSize > theSpaceAvailable) {
  79. /* 
  80.  * There is insufficient free space on the volume to do the safe-save.
  81.  *
  82.  * See if there is anything in the Trash on this volume.
  83.  */
  84.             theError = GetTrashSpace(theOriginalFile.vRefNum,
  85.                 &theTrashSpace);
  86.             if (theError == noErr) {
  87.                 if (theFileSize > 
  88.                         (theSpaceAvailable + theTrashSpace)) {
  89. /*
  90.  * Not enough space even if we empty Trash.
  91.  */
  92.                     (void)StopAlert(rNotEnoughSpaceAlert, nil);
  93.                 } else {
  94.                     Str255 theTrashSpaceString;
  95.                     Str255 theVolumeName;
  96. /*
  97.  * If the user emptied the Trash, we would have enough space.
  98.  */
  99.                     NumToString(theTrashSpace / kKilobytesPerByte, 
  100.                         theTrashSpaceString);
  101.                     theError = GetVolumeName(theOriginalFile.vRefNum, 
  102.                         theVolumeName);
  103.                     if (theError == noErr) {
  104.                         ParamText(theVolumeName, theTrashSpaceString, 
  105.                             kNullString, kNullString);
  106.                     } else {
  107.                         ParamText("\pthe volume", theTrashSpaceString,
  108.                             kNullString, kNullString);
  109.                     }
  110.                     (void)StopAlert(rEmptyTrashAlert, nil);
  111.                 }
  112. /*
  113.  * In either case, the user needs to take further action before we can
  114.  * save. Notify the caller that the user cancelled so that we do not
  115.  * mark the document clean.
  116.  */
  117.                 theError = userCanceledErr;
  118.             }
  119.         } else {
  120. /*
  121.  * There is enough free space on the volume to do the safe-save.
  122.  *
  123.  * We need to create a temporary file. We would like to put it in
  124.  * the Temporary Items folder but it may not be available. See if it
  125.  * is.
  126.  */
  127.             theError = FindFolder(theOriginalFile.vRefNum, 
  128.                 kTemporaryFolderType, kCreateFolder, 
  129.                 &theTemporaryItemsVRefNum, &theTemporaryItemsDirID);
  130.             if (theError == dupFNErr) {
  131. /*
  132.  * This means that the Temporary Items folder IS supported but there 
  133.  * is a file by that name where the folder should be. Wierd!
  134.  *
  135.  * We will create the temporary file in the same folder as the 
  136.  * original file.
  137.  */
  138.                 theTemporaryDirID = theOriginalFile.parID;
  139.                 theError = noErr;
  140.             } else if (theError == fnfErr) {
  141. /*
  142.  * The volume does not support the Temporary Items folder (for some 
  143.  * reason or other).
  144.  *
  145.  * We will create the temporary file in the same folder as the 
  146.  * original file.
  147.  */
  148.                 theTemporaryDirID = theOriginalFile.parID;
  149.                 theError = noErr;
  150.             } else if (theError == noErr) {
  151. /*
  152.  * The volume does support the Temporary Items folder so we will 
  153.  * create the temporary file there.
  154.  */
  155.                 theTemporaryDirID = theTemporaryItemsDirID;
  156.             } else {
  157. /*
  158.  * Some other error occurred. It could be paramErr (-50) which would 
  159.  * indicate a programming error, either here (heaver forbid) or in the
  160.  * caller. In any case we just return the error.
  161.  */
  162.                 theTemporaryDirID = 0; /* to prevent unwanted warnings */
  163.             }
  164.         }
  165.         if (theError == noErr) {
  166. /*
  167.  * We are about to create the temporary file, but first we have to
  168.  * give it a name.
  169.  */
  170.             theError = GetTemporaryName(theTemporaryName);
  171.         }
  172.         if (theError == noErr) {
  173. /*
  174.  * Make an FSSpec out of what we have so far.
  175.  */
  176.             theError = FSMakeFSSpec(theOriginalFile.vRefNum, 
  177.                 theTemporaryDirID, theTemporaryName, 
  178.                 &theTemporaryFile);
  179. /*
  180.  * If this worked, the error will be file not found.
  181.  */
  182.             if (theError == fnfErr) {
  183. /*
  184.  * Get the creator for the temporary file (and the file type). This
  185.  * should be the same as the file we are replacing. This way, if
  186.  * something should go wrong, the user may be able to salvage part of
  187.  * the file.
  188.  */
  189.                 theError = DSGetCreatorAndFileType(aWindow,
  190.                     &theTemporaryCreator, &theTemporaryFileType);
  191.             } else {
  192. /*
  193.  * This really ought to be a do {} while (theError != fnfErr); 
  194.  */
  195.                 DebugStr("\pDuplicate Temporary File Name!");
  196.             }
  197.         }
  198.         if (theError == noErr) {
  199. /*
  200.  * Go ahead and create the temporary file.
  201.  */
  202.             theError = FSpCreate(&theTemporaryFile, 
  203.                 theTemporaryCreator, theTemporaryFileType, 
  204.                 smSystemScript); /* smCurrentScript might be 
  205.                                     better */
  206.         }
  207.         if (theError == noErr) {
  208. /*
  209.  * Create a resource fork in the file, too. It might end up being
  210.  * empty, but we do not really care.
  211.  */
  212.             FSpCreateResFile(&theTemporaryFile, 
  213.                 theTemporaryCreator, theTemporaryFileType, 
  214.                 smSystemScript); /* smCurrentScript might be 
  215.                                     better */
  216.             theError = ResError();
  217.         }
  218.         if (theError == noErr) {
  219. /*
  220.  * Open the data fork of the file we just created.
  221.  */
  222.             theError = FSpOpenDF(&theTemporaryFile, 
  223.                 fsRdWrPerm, &theTemporaryDFRefNum);
  224.         }
  225.         if (theError == noErr) {
  226. /*
  227.  * Open the resource fork of the file we just created.
  228.  */
  229.             theTemporaryRFRefNum = FSpOpenResFile(&theTemporaryFile, 
  230.                 fsRdWrPerm);
  231.             theError = ResError();
  232.         }
  233.         if (theError == noErr) {
  234. /*
  235.  * We will call-back to the caller to actually save the information to
  236.  * the file, but there may be things in the resource fork of the file
  237.  * that the application is not aware of ('ckid', 'MPSR', etc.). First
  238.  * we copy all of the resources in the resource fork of the original 
  239.  * file that the application does not say that it will write into our 
  240.  * temporary file.
  241.  */
  242.             theError = CopyUnknownResTypes(theOriginalRFRefNum, 
  243.                 theTemporaryRFRefNum, aTypeList, aTypeCount);
  244.         }
  245.         if (theError == noErr) {
  246. /*
  247.  * Write the data fork data.
  248.  */
  249.             if (aWriteDFProc != nil) {
  250.                 theError = aWriteDFProc(aWindow, 
  251.                     theTemporaryDFRefNum);
  252.             } 
  253.         }
  254.         if (theError == noErr) {
  255. /*
  256.  * Write the resource fork data.
  257.  */
  258.             if (aWriteRFProc != nil) {
  259.                 theError = aWriteRFProc(aWindow, 
  260.                     theTemporaryRFRefNum);
  261.             } 
  262.         }
  263.         if (theError == noErr) {
  264. /*
  265.  * Exchange the file control blocks for the two files
  266.  */
  267.             theError = FSpExchangeFiles(&theOriginalFile, 
  268.                 &theTemporaryFile);
  269.         }
  270.         if (theError == noErr) {
  271. /*
  272.  * Close the data fork of the file in the Temporary Items folder.
  273.  */
  274.             if (theOriginalDFRefNum != 0) {
  275.                 theError = FSClose(theOriginalDFRefNum);
  276.             }
  277.         }
  278.         if (theError == noErr) {
  279. /*
  280.  * Close the resource fork of the file in the Temporary Items folder.
  281.  */
  282.             if (theOriginalRFRefNum != 0) {
  283.                 CloseResFile(theOriginalRFRefNum);
  284.                 theError = ResError();
  285.             }
  286.         }
  287.         if (theError == noErr) {
  288. /*
  289.  * At this point, the file reference number of the original file
  290.  * points to the file control block of the temporary file (this is
  291.  * a good thing).
  292.  *
  293.  * Delete the file that is in the Temporary Items folder (which is
  294.  * really the original file).
  295.  */
  296.             theError = FSpDelete(&theTemporaryFile);
  297.         }
  298.         if (theError == noErr) {
  299. /*
  300.  * Tell the application to use the data fork reference number of the
  301.  * temporary file (which contains the new data).
  302.  */
  303.             theError = DSSetWindowDFRefNum(aWindow, 
  304.                 theTemporaryDFRefNum);
  305.         }
  306.         if (theError == noErr) {
  307. /*
  308.  * Tell the application to use the resource fork reference number of 
  309.  * the temporary file (which contains the new data).
  310.  */
  311.             theError = DSSetWindowRFRefNum(aWindow, 
  312.                 theTemporaryRFRefNum);
  313.         }
  314.     }
  315.     return theError;
  316. }
  317.  
  318. OSErr CopyUnknownResTypes(short aSourceResFile, 
  319.         short aDestinationResFile, ResType *aTypeList, 
  320.         short aTypeCount) {
  321.     OSErr theError = noErr;
  322.     short theSavedResFile;
  323.     Handle theResource;
  324.     short theResourceID;
  325.     ResType theResourceType;
  326.     Str255 theResourceName;
  327.     short i, j;
  328.     short theNumberOfTypes, theNumberOfResources;
  329.     
  330.     if (aSourceResFile != 0) { /* If there IS one! */
  331.         theSavedResFile = CurResFile();
  332.         UseResFile(aSourceResFile);
  333.         theNumberOfTypes = Count1Types();
  334.         for (i = 1; i <= theNumberOfTypes; i++) {
  335.             Get1IndType(&theResourceType, i);
  336.             if (!InTypeList(theResourceType, aTypeCount, aTypeList)) {
  337.                 theNumberOfResources = 
  338.                     Count1Resources(theResourceType);
  339.                 for (j = 1; j <= theNumberOfResources; j++) {
  340.                     theResource = Get1IndResource(theResourceType, j);
  341.                     if (theResource != nil) {
  342.                         GetResInfo(theResource, &theResourceID, 
  343.                             &theResourceType, theResourceName);
  344.                         DetachResource(theResource);
  345.                         UseResFile(aDestinationResFile);
  346.                         AddResource(theResource, theResourceType, 
  347.                             theResourceID, theResourceName);
  348.                         UseResFile(aSourceResFile);
  349.                     }
  350.                 }
  351.             }
  352.         }
  353.         UseResFile(theSavedResFile);
  354.     }
  355.     return theError;
  356. }
  357.  
  358. OSErr DSGetFileSize(unsigned long *aFileSize) {
  359. /*
  360.  * This should be a DSDocumentList routine which calls back to the 
  361.  * document.
  362.  */
  363.     OSErr theError = noErr;
  364.     
  365.     *aFileSize = 0;
  366.     return theError;
  367. }
  368.  
  369. OSErr GetTrashSpace(short aVRefNum, unsigned long *aTrashSpace) {
  370.     OSErr theError = noErr;
  371.     FSSpec theTrash;
  372.  
  373.     theError = GetTrashFolder(aVRefNum, &theTrash);
  374.     if (theError == noErr) {
  375.         FSGetFolderSize(&theTrash, aTrashSpace);
  376.     } else {
  377.         *aTrashSpace = 0;
  378.     }
  379.     return theError;
  380. }
  381.  
  382. OSErr GetTemporaryName(Str255 theTemporaryName) {
  383.     OSErr theError = noErr;
  384.     unsigned long theTime;
  385.     Str255 theName;
  386.     
  387.     GetDateTime(&theTime);
  388.     NumToString(theTime, theName);
  389.     BlockMoveData(theName, theTemporaryName, (theName[0] + 1));
  390.     return theError;
  391. }
  392.  
  393. Boolean InTypeList(ResType aType, short aTypeCount, ResType *aList) {
  394.     Boolean inTypeList;
  395.     short i;
  396.     
  397.     inTypeList = false;
  398.     for (i=0; i<aTypeCount; i++) {
  399.         if (aType == aList[i]) {
  400.             inTypeList = true;
  401.             break;
  402.         }
  403.     }
  404.     return inTypeList;
  405. }
  406.  
  407.